﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.IO;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using Jayden.Dll.Core.IO;
using Jayden.Dll.Core.Persistence;

namespace Jayden.Dll.Series.Providers
{
	public class EpguidesDotCom
	{
		private UnitOfWork m_Uow;
		private string m_BasePath = string.Empty;
		private bool m_CacheFiles = true;
		private bool m_UseCachedFiles = true;

		public EpguidesDotCom(UnitOfWork uow, string basePath = "", bool cacheFiles = true, bool useCachedFiles = true)
		{
			m_BasePath = basePath;
			m_CacheFiles = cacheFiles;
			m_UseCachedFiles = useCachedFiles;
			m_Uow = uow;
		}

		public class WebClient : System.Net.WebClient
		{
			protected override WebRequest GetWebRequest(Uri address)
			{
				WebRequest request = base.GetWebRequest(address);
				request.Timeout = 20 * 60 * 1000;
				return request;
			}
		}

		DataTable m_AllShows = null;
		public DataTable AllShows(bool useCache = true)
		{
			if (m_AllShows != null && useCache)
				return m_AllShows;
			WebClient client = new WebClient();
			string physicalPath = m_BasePath + "allshows.txt";
			byte[] datas = null;
			if (useCache && File.Exists(physicalPath))
				datas = File.ReadAllBytes(physicalPath);
			else
			{
				datas = client.DownloadData("http://epguides.com/common/allshows.txt");
				if(m_CacheFiles)
					File.WriteAllBytes(physicalPath, datas);
			}
			Stream stream = new MemoryStream(datas);
			CsvStream csvStream = new CsvStream(stream);

			DataTable allShows = new DataTable();
			string[] values = csvStream.ReadCsvLine();
			if (values != null)
			{
				foreach (string columnName in values)
				{
					DataColumn column = new DataColumn(columnName, typeof(string));
					allShows.Columns.Add(column);
				}
				values = csvStream.ReadCsvLine();
			}
			while (values != null)
			{
				DataRow row = allShows.Rows.Add(values);
				values = csvStream.ReadCsvLine();
			}
			m_AllShows = allShows;
			return allShows;
		}

		private Dictionary<string, DataTable> m_AllEpisodes = new Dictionary<string, DataTable>();
		public DataTable AllEpisodes(string id)
		{
			DataTable allEpisodes = null;
			if (m_UseCachedFiles && m_AllEpisodes.TryGetValue(id, out allEpisodes))
				return allEpisodes;
			try
			{
				string url = string.Format("http://epguides.com/common/exportToCSV.asp?rage={0}", id);
				string physicalPath = string.Format(m_BasePath + "episodes.rage.{0}.txt", id);
				string html = null;
				if (m_UseCachedFiles && File.Exists(physicalPath))
				{
					html = File.ReadAllText(physicalPath);
				}
				else
				{
					WebClient client = new WebClient();
					html = client.DownloadString(url);
					if(m_CacheFiles)
						File.WriteAllText(physicalPath, html);
				}
				int start = html.IndexOf("<pre>");
				int end = html.IndexOf("</pre>", start);
				if (start != -1 && end != -1)
				{
					start += 5;
					string dataString = html.Substring(start, end - start).Trim();
					if (string.IsNullOrWhiteSpace(dataString) || dataString == "no data")
						return null;
					byte[] data = Encoding.UTF8.GetBytes(dataString);
					MemoryStream stream = new MemoryStream(data);
					CsvStream csvStream = new CsvStream(stream);
					allEpisodes = new DataTable();
					int numberOfColumns = 8;
					string[] values = csvStream.ReadCsvLine();
					values = CutValues(numberOfColumns, values);
					if (values != null)
					{
						foreach (string columnName in values)
						{
							DataColumn column = new DataColumn(columnName, typeof(string));
							allEpisodes.Columns.Add(column);
						}
						values = csvStream.ReadCsvLine();
					}
					while (values != null)
					{
						DataRow row = allEpisodes.Rows.Add(CutValues(numberOfColumns, values));
						values = csvStream.ReadCsvLine();
					}
					if (m_CacheFiles)
					{
						m_AllEpisodes.Remove(id);
						m_AllEpisodes.Add(id, allEpisodes);
					}
					return allEpisodes;
				}
				return null;
			}
			catch (Exception e)
			{
				return null;
			}
		}

		public string[] CutValues(int maxCount, string[] values)
		{
			if (maxCount == values.Length)
				return values;
			string[] newValues = new string[maxCount];
			int max = Math.Min(newValues.Length, values.Length);
			for (int c = 0; c < max; c++)
				newValues[c] = values[c];
			return newValues;
		}

		public IEnumerable<ShowInfo> Shows(FilterShowDelegate filter = null)
		{
			DataTable allShows = AllShows();
			HashSet<string> ids = new HashSet<string>();
			foreach (DataRow showRow in allShows.Rows)
			{
				string id = showRow["tvrage"] as string;
				if (!(string.IsNullOrWhiteSpace(id) || ids.Contains(id)))
				{
					ids.Add(id);
					ShowInfo showInfo = new ShowInfo(m_Uow);
					showInfo.Title = showRow["title"] as string;
					showInfo.Id = id;
					showInfo.StartDate = ParseDate(showRow["start date"] as string);
					showInfo.EndDate = ParseDate(showRow["end date"] as string);
					showInfo.RunTime = ParseRunTime(showRow["run time"] as string);

					if (filter == null || !filter(showInfo))
					{
						DataTable allEpisodes = AllEpisodes(id);
						if (allEpisodes != null)
						{
							List<EpisodeInfo> episodes = showInfo.Episodes = new List<EpisodeInfo>();

							foreach (DataRow episodeRow in allEpisodes.Rows)
							{
								EpisodeInfo episodeInfo = new EpisodeInfo(m_Uow);
								episodeInfo.ShowInfoId = showInfo.Id;
								episodeInfo.AirDate = ParseDate(episodeRow["airdate"] as string);
								episodeInfo.Season = ParseNumber(episodeRow["season"] as string);
								episodeInfo.Episode = ParseNumber(episodeRow["episode"] as string);
								episodeInfo.Number = ParseNumber(episodeRow["number"] as string);
								episodeInfo.Title = episodeRow["title"] as string;
								episodeInfo.Special = ParseBoolean(episodeRow["special?"] as string);
								episodeInfo.Code = episodeRow["production code"] as string;
								episodes.Add(episodeInfo);
							}
						}
						yield return showInfo;
					}
				}
			}
		}

		private bool ParseBoolean(string value)
		{
			value = value.ToLower();
			if (value == "n")
				return false;
			if (value == "y")
				return true;
			return true;
		}

		private int ParseRunTime(string value)
		{
			Regex std = new Regex("(\\d+) min");
			Match match = std.Match(value);
			if (match.Success)
				return int.Parse(match.Groups[1].Value);

			return -1;
		}

		private int? ParseNumber(string value)
		{
			if (string.IsNullOrWhiteSpace(value))
				return null;
			Regex std = new Regex("\\d+");
			Match match = std.Match(value);
			if (match.Success && match.Groups.Count == 1)
				return int.Parse(match.Groups[0].Value);
			return int.Parse(value);
		}

		private DateTime? ParseDate(string value)
		{
			try
			{
				if (value != null)
					value = value.Replace("?", "").Replace("_", "").Trim();
				if (string.IsNullOrWhiteSpace(value))
					return null;
				return DateTime.Parse(value, CultureInfo.InvariantCulture);
			}
			catch (Exception e)
			{
				try
				{
					int year = int.Parse(value, CultureInfo.InvariantCulture);
					return new DateTime(year, 1, 1);
				}
				catch (Exception f)
				{
					return null;
				}
			}
		}
	}
}
